/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.muni.fi.diplomka.datalayer.daoimpl;

import cz.fi.muni.diplomka.common.Coordinates;
import cz.fi.muni.diplomka.common.enumeration.BuildingType;
import cz.muni.fi.diplomka.datalayer.dao.BuildingInCityDao;
import cz.muni.fi.diplomka.datalayer.entity.BuildingInCityEntity;
import cz.muni.fi.diplomka.datalayer.util.DbConnector;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.neo4j.cypher.javacompat.ExecutionEngine;
import org.neo4j.cypher.javacompat.ExecutionResult;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;

/**
 *
 * @author Jakub Kutil
 */
public class BuildingInCityDaoImpl implements BuildingInCityDao {

    @Override
    public void addBuildingInCity(BuildingInCityEntity buildingInCityEntity) {
        if (buildingInCityEntity == null) {
            throw new IllegalArgumentException("Building In City Entity can not be null.");
        }

        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();

        try (Transaction t = gds.beginTx()) {

            Map<String, Object> params = new HashMap<>();

            params.put("cityId", buildingInCityEntity.getCityId());
            params.put("xCoordinate", buildingInCityEntity.getCoordinates().getX_coordinate());
            params.put("yCoordinate", buildingInCityEntity.getCoordinates().getY_coordinate());
            params.put("level", buildingInCityEntity.getLevel());
            params.put("name", buildingInCityEntity.getName().toString());
            String query = "MATCH (City:City {id : {cityId}})"
                    + "CREATE (BuildingInCity: BuildingInCity {xCoordinate : {xCoordinate}, yCoordinate : {yCoordinate}, "
                    + "level : {level}, name : {name}})<-[r:IN]-(City)";
            engine.execute(query, params);
            t.success();
        }
    }

    @Override
    public void editBuildingInCity(BuildingInCityEntity buildingInCityEntity) {
        if (buildingInCityEntity == null) {
            throw new IllegalArgumentException("Building In City Entity can not be null.");
        }

        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();

        try (Transaction t = gds.beginTx()) {

            Map<String, Object> params = new HashMap<>();

            params.put("cityId", buildingInCityEntity.getCityId());
            params.put("xCoordinate", buildingInCityEntity.getCoordinates().getX_coordinate());
            params.put("yCoordinate", buildingInCityEntity.getCoordinates().getY_coordinate());
            params.put("level", buildingInCityEntity.getLevel());
            params.put("name", buildingInCityEntity.getName().toString());

            String query = "MATCH (City:City {id : {cityId} })-[r:IN]->(BuildingInCity:BuildingInCity { xCoordinate : {xCoordinate}, yCoordinate : {yCoordinate}})"
                    + " SET BuildingInCity.level = {level}, BuildingInCity.name = {name}";
            engine.execute(query, params);
            t.success();
        }
    }

    @Override
    public void deleteBuildingInCity(Long cityId, Coordinates coordinates) {
        if (cityId == null) {
            throw new IllegalArgumentException("City Id can not be null.");
        }
        if (coordinates == null) {
            throw new IllegalArgumentException("Coordinates can not be null.");
        }
        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();

        try (Transaction t = gds.beginTx()) {

            Map<String, Object> params = new HashMap<>();

            params.put("cityId", cityId);
            params.put("xCoordinate", coordinates.getX_coordinate());
            params.put("yCoordinate", coordinates.getY_coordinate());

            String query = "MATCH (City:City{ id : {cityId}})-[r:IN]->"
                    + "(BuildingInCity:BuildingInCity {xCoordinate : {xCoordinate}, yCoordinate : {yCoordinate}}) "
                    + "DELETE BuildingInCity,r";
            engine.execute(query, params);
            t.success();
        }
    }

    @Override
    public BuildingInCityEntity getBuildingInCityAtPosition(Long cityId, Coordinates coordinates) {
        if (cityId == null) {
            throw new IllegalArgumentException("City Id can not be null.");
        }
        if (coordinates == null) {
            throw new IllegalArgumentException("Coordinates can not be null.");
        }
        BuildingInCityEntity buildingInCityEntity = new BuildingInCityEntity();

        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();


        try (Transaction t = gds.beginTx()) {
            Map<String, Object> params = new HashMap<>();

            params.put("cityId", cityId);
            params.put("xCoordinate", coordinates.getX_coordinate());
            params.put("yCoordinate", coordinates.getY_coordinate());

            String query = "MATCH (City:City{ id : {cityId}})-[r:IN]->(BuildingInCity:BuildingInCity {xCoordinate : {xCoordinate}, yCoordinate : {yCoordinate}}) RETURN BuildingInCity";
            ExecutionResult result = engine.execute(query, params);
            Iterator<Node> buildings = result.columnAs("BuildingInCity");
            while (buildings.hasNext()) {
                Node node = buildings.next();

                buildingInCityEntity.setCityId(cityId);
                Coordinates c = new Coordinates();
                c.setX_coordinate(Integer.valueOf(node.getProperty("xCoordinate").toString()));
                c.setY_coordinate(Integer.valueOf(node.getProperty("yCoordinate").toString()));
                buildingInCityEntity.setCoordinates(c);
                buildingInCityEntity.setLevel(Integer.valueOf(node.getProperty("level").toString()));
                buildingInCityEntity.setName(BuildingType.valueOf(node.getProperty("name").toString()));

            }
            t.success();
        }
        return buildingInCityEntity;
    }

    @Override
    public List<BuildingInCityEntity> getAllBuildingInCity(Long cityId) {
        if (cityId == null) {
            throw new IllegalArgumentException("City Id can not be null.");
        }
        List<BuildingInCityEntity> buildingList = new ArrayList<>();
        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();


        try (Transaction t = gds.beginTx()) {
            Map<String, Object> params = new HashMap<>();

            params.put("cityId", cityId);

            String query = "MATCH (City:City{ id : {cityId}})-[r:IN]->(BuildingInCity:BuildingInCity) RETURN BuildingInCity";
            ExecutionResult result = engine.execute(query, params);
            Iterator<Node> buildings = result.columnAs("BuildingInCity");
            while (buildings.hasNext()) {
                Node node = buildings.next();

                BuildingInCityEntity buildingInCityEntity = new BuildingInCityEntity();
                buildingInCityEntity.setCityId(cityId);
                Coordinates c = new Coordinates();
                c.setX_coordinate(Integer.valueOf(node.getProperty("xCoordinate").toString()));
                c.setY_coordinate(Integer.valueOf(node.getProperty("yCoordinate").toString()));
                buildingInCityEntity.setCoordinates(c);
                buildingInCityEntity.setLevel(Integer.valueOf(node.getProperty("level").toString()));
                buildingInCityEntity.setName(BuildingType.valueOf(node.getProperty("name").toString()));

                buildingList.add(buildingInCityEntity);
            }
            t.success();
        }
        return buildingList;
    }

    @Override
    public List<BuildingInCityEntity> getAllBuildingInCityOfType(Long cityId, BuildingType buildingType) {
        if (cityId == null) {
            throw new IllegalArgumentException("City Id can not be null.");
        }
        if (buildingType == null) {
            throw new IllegalArgumentException("Building Type can not be null.");
        }
        List<BuildingInCityEntity> buildingList = new ArrayList<>();
        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();


        try (Transaction t = gds.beginTx()) {
            Map<String, Object> params = new HashMap<>();

            params.put("cityId", cityId);
            params.put("buildingType", buildingType.toString());

            String query = "MATCH (City{ id : {cityId}})-[r:IN]->(BuildingInCity:BuildingInCity {name : {buildingType}}) RETURN BuildingInCity";
            ExecutionResult result = engine.execute(query, params);
            Iterator<Node> buildings = result.columnAs("BuildingInCity");
            while (buildings.hasNext()) {
                Node node = buildings.next();

                BuildingInCityEntity buildingInCityEntity = new BuildingInCityEntity();
                buildingInCityEntity.setCityId(cityId);
                Coordinates c = new Coordinates();
                c.setX_coordinate(Integer.valueOf(node.getProperty("xCoordinate").toString()));
                c.setY_coordinate(Integer.valueOf(node.getProperty("yCoordinate").toString()));
                buildingInCityEntity.setLevel(Integer.valueOf(node.getProperty("level").toString()));
                buildingInCityEntity.setName(BuildingType.valueOf(node.getProperty("name").toString()));

                buildingList.add(buildingInCityEntity);
            }
            t.success();
        }
        return buildingList;
    }
}
